'use strict';

const Service = require('egg').Service;
const qiniu = require('qiniu');
const COS = require('cos-nodejs-sdk-v5');
const fs = require('fs');
const _ = require('lodash');
const dayjs = require('dayjs');

class TopicSvc extends Service {


  /**
   * 查询文章列表
   */
  async selectTopics(query, opt) {
    query.is_public = true;
    const result = await this.ctx.model.Topic.find(query, {content: 0}, opt);
    return result;
  }


  async getDeliveryTopics(kw, last_delivery_at) {
    const keywords = kw.trim().split(/\s+/).map(keyword => new RegExp(keyword, 'i'));
    const keywordConditions = keywords.map(keyword => ({title: {$regex: keyword}}));

    let query;
    if (last_delivery_at) {
      query = {$and: [...keywordConditions, {is_public: true, updatedAt: {'$gte': last_delivery_at}}]};
    } else {
      query = {$and: [...keywordConditions, {is_public: true}]};
    }

    const result = await this.ctx.model.Topic
      .find(query)
      .sort({updatedAt: -1})
      .limit(20)
      .hint({title: 1, updatedAt: -1})
      .lean();
    return result;
  }


  async countTopics(query) {
    let result;
    query.is_public = true;

    if (query.title) {
      query.title = {'$regex': new RegExp(query.title.replace(/\s+/g, '.*'), 'i')};
    }

    result = await this.ctx.model.Topic.count(query);
    return result;
  }


  /*
   * 根据主题ID获取主题
   * @param {String} id 主题ID
   */
  async getTopicById(id) {
    const topic = await this.ctx.model.Topic.findOne({_id: id}).exec();
    if (!topic) {
      return {
        topic: null,
        author: null,
        last_reply: null,
      };
    }

    const author = await this.service.userSvc.getUserById(topic.author_id);

    let last_reply = null;
    if (topic.last_reply) {
      last_reply = await this.service.replySvc.getReplyById(topic.last_reply);
    }

    return {
      topic,
      author,
      last_reply,
    };
  }

  /*
 * 获取关键词能搜索到的主题数量
 * @param {String} query 搜索关键词
 * @param {Object} show_private 展示私有内容
 */
  async getCountByQuery(query, opt, show_private) {
    try {
      if (query.title && query.title instanceof Array) {  // 有title表示是搜索
        let count = null;
        console.time('sean-getCountByQuery');
        let keywordArray = query.title;
        if (keywordArray.length == 1) {
          let keyword = keywordArray[0];
          let query = {title: {'$regex': new RegExp(keyword, 'i')}};
          if (!show_private) {
            query.is_public = true;
          }
          // count = await this.ctx.model.Topic.count(query).hint({title: 1, updatedAt: -1}).exec();
          count = await this.ctx.model.Topic.count(query).exec();
        } else {
          let where_in = [];
          for (let i = 0; i < keywordArray.length; i++) {
            let keyword = keywordArray[i];
            where_in.push(new RegExp(keyword, 'i'));
          }
          let query_$in = {'title': {'$in': where_in}};
          if (!show_private) {
            query_$in.is_public = true;
          }
          count = await this.ctx.model.Topic.count(query_$in).exec();
        }
        console.timeEnd('sean-getCountByQuery');
        return count;
      } else {
        if (!show_private) {
          query.is_public = true;
        }
        return this.ctx.model.Topic.count(query).exec();
      }
    } catch (e) {
      this.logger.warn('搜索本地时出现异常', e.message);
      return 0;
    }
  }

  /*
   * 查询文章列表
   */
  async getTopicsByQuery(query, opt, show_private) {
    let topics = [];
    try {
      if (query.title && query.title instanceof Array) {  // 有title表示是搜索
        let keywordArray = query.title;
        if (keywordArray.length == 1) {
          let keyword = keywordArray[0];
          let query = {title: {'$regex': new RegExp(keyword, 'i')}};
          if (!show_private) {
            query.is_public = true;
          }
          console.time('sean-getTopicsByQuery');
          // topics = await this.ctx.model.Topic.find(query, {}, opt).hint({title: 1, updatedAt: -1}).exec();
          topics = await this.ctx.model.Topic.find(query, {}, opt).exec();
          console.timeEnd('sean-getTopicsByQuery');
        } else {
          let where_in = [];
          for (let i = 0; i < keywordArray.length; i++) {
            let keyword = keywordArray[i];
            where_in.push(new RegExp(keyword, 'i'));
          }
          let query_$in = {'title': {'$in': where_in}};
          if (!show_private) {
            query_$in.is_public = true;
          }
          topics = await this.ctx.model.Topic.find(query_$in, {}, opt).exec();
        }
      } else {
        if (!show_private) {
          query.is_public = true;
        }

        if (query.need_omit == true) {
          topics = await this.ctx.model.Topic.find(query, {pic: 0, intro: 0}, opt).exec();
        } else {
          topics = await this.ctx.model.Topic.find(query, {}, opt).exec();
        }
      }
      if (topics.length === 0) {
        return [];
      }
    } catch (e) {
      this.logger.warn('搜索本地时出现异常', e.message);
    } finally {
      return topics;
    }
  }


  // for sitemap
  getLimit5w() {
    const query = {};
    const opts = {limit: 50000, sort: '-createdAt'};
    return this.ctx.model.Topic.find(query, '_id', opts).exec();
  }

  /*
   * 获取所有信息的主题
   * Callback:
   * - err, 数据库异常
   * - message, 消息
   * - topic, 主题
   * - author, 主题作者
   * - replies, 主题的回复
   * @param {String} id 主题ID
   * @param {Function} callback 回调函数
   */
  async getFullTopic(id) {
    const query = {_id: id};
    const topic = await this.ctx.model.Topic.findOne(query);

    if (!topic) {
      // throw new Error('此话题不存在或已被删除。');
      return [];
    }

    topic.linkedContent = await this.service.atSvc.linkUsers(topic.content);

    const author = await this.service.userSvc.getUserById(topic.author_id);
    if (!author) {
      // throw new Error('话题的作者丢了。');
      return [];
    }

    const replies = await this.service.replySvc.getRepliesByTopicId(topic.id);
    return [topic, author, replies];
  }

  /*
   * 更新主题的最后回复信息
   * @param {String} topicId 主题ID
   * @param {String} replyId 回复ID
   * @param {Function} callback 回调函数
   */
  updateLastReply(topicId, replyId) {
    const update = {
      last_reply: replyId,
      last_reply_at: new Date(),
      $inc: {
        reply_count: 1,
      },
    };
    const opts = {new: true};
    return this.ctx.model.Topic.findByIdAndUpdate(topicId, update, opts).exec();
  }

  /*
   * 根据主题ID，查找一条主题
   * @param {String} id 主题ID
   * @param {Function} callback 回调函数
   */
  getTopic(id) {
    return this.ctx.model.Topic.findOne({_id: id}).exec();
  }

  /*
   * 将当前主题的回复计数减1，并且更新最后回复的用户，删除回复时用到
   * @param {String} id 主题ID
   */
  async reduceCount(id) {
    const update = {$inc: {reply_count: -1}};
    const reply = await this.service.replySvc.getLastReplyByTopicId(id);
    if (reply) {
      update.last_reply = reply._id;
    } else {
      update.last_reply = null;
    }
    const opts = {new: true};

    const topic = await this.ctx.model.Topic.findByIdAndUpdate(id, update, opts).exec();
    if (!topic) {
      throw new Error('该主题不存在');
    }

    return topic;
  }

  incrementVisitCount(id) {
    const query = {_id: id};
    const update = {$inc: {visit_count: 1}};
    return this.ctx.model.Topic.findByIdAndUpdate(query, update).exec();
  }

  incrementVisitCount4API(id) {
    const query = {_id: id, tab: {$nin: ['original', 'daily']}};
    const update = {$inc: {visit_count: 1}};
    return this.ctx.model.Topic.findOneAndUpdate(query, update).exec();
  }

  incrementCollectCount(id) {
    const query = {_id: id};
    const update = {$inc: {collect_count: 1}};
    return this.ctx.model.Topic.findByIdAndUpdate(query, update).exec();
  }

  decrementCollectCount(id) {
    const query = {_id: id};
    const update = {$inc: {collect_count: -1}};
    return this.ctx.model.Topic.findByIdAndUpdate(query, update).exec();
  }

  async newAndSave(title, content, tab, authorId, original_url, is_public, topic_type, tags, intro) {
    if (is_public == undefined) {
      is_public = true;
    }
    const topic = new this.ctx.model.Topic();
    topic.title = title;
    topic.content = content;
    topic.tab = tab;
    topic.is_public = is_public;
    topic.intro = intro;

    const author = await this.service.userSvc.getUserById(authorId);
    topic.author_id = authorId;
    topic.author_name = author.loginname;
    topic.author_avatar = author.avatar_url;


    if (original_url) {
      topic.original_url = original_url;
    }
    if (tab == 'original') {
      topic.intro = content.replace(/[^_/:：,.;'，。、；a-zA-Z0-9\u4e00-\u9fa5]/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 300);
    }
    if (topic_type) {
      topic.topic_type = topic_type;
    }
    if (tags) {
      if (Array.isArray(tags)) {
        topic.tags = Array.from(new Set(tags));
      } else {
        topic.tags = Array.from(new Set(tags.trim().split(/\s+/)));
      }
      topic.tags = this.ctx.helper.getUniqueArrayCaseNotSensitive(topic.tags);
    }

    return topic.save();
  }


  async upsert(title, content, tab, author_id, original_url, is_public, intro) {
    const author = await this.service.userSvc.getUserById(author_id);
    const author_name = author.loginname;
    const author_avatar = author.avatar_url;
    let now = Date.now();

    // 防止收藏时标题重复: 末位自增+1
    let alreadyTopicCount = await this.ctx.model.Topic.count({
      author_id,
      title: new RegExp('^' + title),
      tab,
      original_url: {$ne: original_url}
    });
    if (alreadyTopicCount > 0) {
      title += `(${alreadyTopicCount})`;
    }

    let upsertModel = {
      title,
      content,
      tab,
      author_id,
      author_name,
      author_avatar,
      original_url,
      is_public,
      intro,
      updatedAt: now,
      $setOnInsert: {
        createdAt: now,
        last_reply_at: now,
        top: false,
        good: false,
        lock: false,
        reply_count: 0,
        visit_count: 0,
        collect_count: 0
      }
    };


    return this.ctx.model.Topic.findOneAndUpdate({
      author_id,
      tab,
      original_url
    }, upsertModel, {upsert: true, new: true});
  }


  async importTopics(topics) {
    if (!topics || topics.length == 0) {
      return 0;
    }
    let success_import_count = 0;
    let successimportTopicsLength = 0;
    let importDate = new Date();

    // 导入历史数据时放开
    // importDate.setFullYear(importDate.getFullYear() - 1);

    topics = topics.filter(v => {
      return (v.title && v.title.length > 1 && v.content && !v.content.includes('chrome-error'));
    });

    for (let i = 0, len = topics.length; i < len; i++) {
      let topic = topics[i];

      if (!topic) {
        continue;
      }

      if (!topic.boards) {
        topic.boards = [];
      }
      if (!topic.tags) {
        topic.tags = [];
      }

      if (topic.is_advert) {
        topics.splice(i--, 1);
        continue;
      }

      if (topic.title) {
        topic.title = topic.title.replace(/\u200B/g, '').trim();
        if (topic.title.startsWith('今日好价')) {
          topics.splice(i--, 1);
          continue;
        }
      }
      if (topic.content) {
        topic.content = topic.content
          .replace('&utm_source=tuicool&utm_medium=referral', '')
          .replace('?utm_source=tuicool&utm_medium=referral&', '?')
          .replace('?utm_source=tuicool&utm_medium=referral', '')
          .trim();
      }
      if (topic.original_url) {
        topic.original_url = topic.original_url
          .replace('&utm_source=tuicool&utm_medium=referral', '')
          .replace('?utm_source=tuicool&utm_medium=referral&', '?')
          .replace('?utm_source=tuicool&utm_medium=referral', '')
          .trim();
      }
      if (topic.intro) {
        topic.intro = topic.intro.trim();
        if (topic.intro.length == 0) {
          topic.intro = undefined;
        }
      }

      if (topic.time) {
        topic.createdAt = topic.updatedAt = topic.last_reply_at = dayjs(topic.time);
      } else {
        topic.createdAt = topic.updatedAt = topic.last_reply_at = new Date(importDate.getTime() - i);
      }
    }

    for (const topic of topics) {
      topic.boards = topic.boards.filter(v => v);
      topic.boards = [...new Set(topic.boards)];
      topic.tags = topic.tags.filter(v => v);
      topic.tags = [...new Set(topic.tags)];

      const title = topic.title.toLowerCase();

      if (topic.tab == 'podcast') {
        topic.tags = [];
      }

      if (topic.tab == 'podcast' && topic.original_url.includes('https://www.ximalaya.com')) {
        topic.content = topic.original_url = topic.content.replace('https://www.ximalaya.com', 'https://m.ximalaya.com');
      }

      if (title.includes('chatgpt')) {
        topic.tags.push('ChatGPT');
      }

      if (topic.tab == 'tech') {
        this.ctx.helper.setTechBoard(topic);
      }

      if (topic.title.includes('早报')) {
        topic.boards.push('早报');
        topic.tags.push('早报');
      }

      if (topic.boards.includes('视频')) {
        topic.is_video = true;
      } else if (topic.tags.includes('视频')) {
        topic.is_video = true;
      } else if (topic.tags.includes('影音精选')) {
        topic.is_video = true;
      } else if (topic.tags.includes('播客')) {
        topic.is_video = true;
      } else if (topic.author_name == '天下足球') {
        topic.is_video = true;
      } else if (topic.author_name == 'Bilibili热榜') {
        topic.is_video = true;
      } else if (topic.author_name == '梨视频') {
        topic.is_video = true;
      }


      // 世界杯专题
      if (topic.title.includes('世界杯')) {
        topic.tags.push('世界杯');
      }


      if (title.includes('chatgpt')
        || title.includes('gpt4')
        || title.includes('gpt-4')
        || title.includes('文心一言')) {
        if (topic.tab != 'tech') {
          topic.boards.push('科技');
        }
      }

      if (topic.tab == 'yutang' && topic.tags.includes('美食')) {
        topic.boards.push('美食');
        _.remove(topic.tags, function (v) {
          return v == '美食';
        });
      }

      if (topic.tab == 'yutang' && topic.tags.includes('百科')) {
        topic.boards.push('百科');
        _.remove(topic.tags, function (v) {
          return v == '百科';
        })
      }

      if (topic.tab == 'yutang' && topic.tags.includes('军事')) {
        topic.boards.push('军事');
        _.remove(topic.tags, function (v) {
          return v == '军事';
        })
      }

      if (topic.tab == 'yutang' && topic.tags.includes('电影')) {
        topic.boards.push('电影');
        _.remove(topic.tags, function (v) {
          return v == '电影';
        })
      }

      if (topic.tab == 'yutang' && topic.tags.includes('健康')) {
        topic.boards.push('健康');
        _.remove(topic.tags, function (v) {
          return v == '健康';
        })
      }

      if (topic.tab == 'yutang' && topic.tags.includes('旅游')) {
        topic.boards.push('旅游');
        _.remove(topic.tags, function (v) {
          return v == '旅游';
        })
      }

      if (topic.tab == 'yutang' && topic.tags.includes('汽车')) {
        topic.boards.push('汽车');
        _.remove(topic.tags, function (v) {
          return v == '汽车';
        })
      }

      if (topic.tab == 'yutang' && topic.tags.includes('财经')) {
        topic.boards.push('财经');
        _.remove(topic.tags, function (v) {
          return v == '财经';
        })
      }

      if (topic.tab == 'yutang' && topic.tags.includes('图片')) {
        topic.boards.push('图片');
        _.remove(topic.tags, function (v) {
          return v == '图片';
        })
      }

      if (topic.author_name == '知乎推荐') {
        topic.boards.push('问答');
      }

      if (topic.content.startsWith('https://daily.zhihu.com/') && !topic.title.includes('瞎扯')) {
        topic.boards = ['百科'];
      }


      if (topic.boards.includes('搞笑')) {
        topic.title = '🤣 ' + topic.title;
      }

      if (topic.content.includes('https://www.zhihu.com/zvideo')) {
        topic.is_video = true;
      }

      if (topic.is_video) {
        topic.boards.push('视频');
      }

      topic.tags = this.ctx.helper.cleanTags(topic.tags);

      topic.boards = topic.boards.filter(v => v);
      topic.boards = [...new Set(topic.boards)];
      topic.tags = topic.tags.filter(v => v);
      topic.tags = [...new Set(topic.tags)];

      if (topic.boards.length > 10) {
        topic.intro = undefined;
      }
      if (topic.pic && topic.pic.includes('pic.rmb.bdstatic.com')) {
        topic.pic = undefined;
      }
    }


    topics.forEach(v => {
      v.tags = v.tags.filter(v => v != '视频');
    })

    this.ctx.helper.setDefaultPic(topics);
    this.ctx.helper.changeTags(topics);

    // 统计导入前的用户总文章数
    let cntBeforeImport = await this.ctx.model.Topic.count();
    try {
      await this.ctx.model.Topic.insertMany(topics, {ordered: false});
    } catch (e) {
      console.warn(e.message);
    } finally {
      // 统计导入后的用户总文章数, 计算真正导入的文章数量
      let cntAfterImport = await this.ctx.model.Topic.count();
      success_import_count = cntAfterImport - cntBeforeImport;

      await this.ctx.service.userSvc.updateUserInfoAfterImportTopics(topics);
      return success_import_count;
    }
  }

  /*
  * 过滤掉已有的数据
  * topics 待插入数据库的数据
  * return 库里没有的数据
  */
  async filterExistTopics(topics) {
    let is_exist_flags = await Promise.all(
      topics.map(async (topic) => {
        let author_name = topic.author_name;
        // title必须要特殊处理, 因为有些过滤是在import之前操作的
        let title = topic.title.replace(/\u200B/g, '').trim();
        let original_url = topic.original_url;
        let tab = topic.tab;
        let is_exist = await this.ctx.model.Topic.count({
          $or: [{title, tab}, {original_url, tab}]
        });
        return (is_exist > 0);
      })
    );

    topics = topics.filter((topic, idx) => !is_exist_flags[idx]);
    return topics;
  }

  /*
  * 过滤掉已有的数据(利用redis缓存判断是否存在, 每次import后, 当前topic的数据会缓存七天)
  * @param {Array} topics 待插入数据库的数据
  * return 库里没有的数据
  */
  async filterExistTopicsInRedis(topics) {
    let is_exist_flags = await Promise.all(
      topics.map(async (topic) => {
        let exist_redis_key = `exist:${topic.author_name}:${topic.title}`;
        let is_exist = await this.service.cacheSvc.exists(exist_redis_key);
        return !!is_exist;
      })
    );

    topics = topics.filter((topic, idx) => !is_exist_flags[idx]);
    return topics;
  }

  /*
   * 七牛上传
   * @param {Stream} readableStream 流
   * @param {String} key 文件名key
   * @param {Function} callback 回调函数
   */
  qnUpload(readableStream, key) {
    const {accessKey, secretKey, bucket} = this.config.qn_access;

    const mac = new qiniu.auth.digest.Mac(accessKey, secretKey);
    const putPolicy = new qiniu.rs.PutPolicy({scope: bucket});
    const uploadToken = putPolicy.uploadToken(mac);

    const config = new qiniu.conf.Config();
    const formUploader = new qiniu.form_up.FormUploader(config);
    const putExtra = new qiniu.form_up.PutExtra();

    return new Promise(function (resolve, reject) {
      formUploader.putStream(uploadToken, key, readableStream, putExtra, function (respErr, respBody, respInfo) {
        if (respErr) {
          reject(respErr);
          return;
        }
        if (respInfo.statusCode === 200) {
          resolve(respBody);
        } else {
          reject(new Error('上传失败:statusCode !== 200'));
        }
      });
    });
  }

  /*
   * 腾讯云cos上传
   * @param {Stream} readableStream 文件流
   * @param {String} filename 文件名
   */
  cosUpload(readableStream, filename) {
    const cos = new COS({
      SecretId: this.config.cos.SecretId,
      SecretKey: this.config.cos.SecretKey
    });

    let cosConfig = {
      Bucket: this.config.cos.Bucket,
      Region: this.config.cos.Region,
      Key: 'taosha/' + filename,
      StorageClass: 'STANDARD',
      Body: readableStream
    };

    return new Promise(function (resolve, reject) {
      cos.putObject(cosConfig, function (err, response) {
        if (err) {
          reject(err);
          return;
        }
        if (response.statusCode == 200) {
          resolve(response.Location);
        } else {
          reject(new Error('上传失败:statusCode !== 200'));
        }
      });
    });
  }


  /**
   * 修改标签名
   * @param {string} author_id 用户id
   * @param {string} old_name 原标签名
   * @param {string} new_name 新标签名
   */
  async updateOriginalTagName(author_id, old_name, new_name) {
    return await this.ctx.model.Topic.updateMany({
      author_id: author_id,
      tags: old_name
    }, {$set: {"tags.$": new_name}});
  }

  /**
   * 删除标签
   * @param {string} author_id 用户id
   * @param {string} name 要删除的标签名
   */
  async removeOriginalTag(author_id, name) {
    return await this.ctx.model.Topic.updateMany({author_id: author_id, tags: name}, {$pull: {tags: name}});
  }


  async listOriginalTags(author_id, name) {
    let cache_key = `original_tags:${author_id}`;
    if (!name) {
      let cache_value = await this.service.cacheSvc.get(cache_key);
      if (cache_value) {
        return cache_value;
      }
    }


    let pipeline = null;
    if (name) {
      pipeline = [
        {$match: {author_id: author_id.toString()}},
        {$unwind: '$tags'},
        {$group: {_id: "$tags", topics_size: {$sum: 1}, sortby: {$max: '$updatedAt'}}},
        {$match: {_id: {'$regex': new RegExp(name, 'i')}}},
        {$sort: {sortby: -1, _id: 1}},
        {$project: {name: '$_id', topics_size: 1, _id: 0}}
      ];
    } else {
      pipeline = [
        {$match: {author_id: author_id.toString()}},
        {$unwind: '$tags'},
        {$group: {_id: "$tags", topics_size: {$sum: 1}, sortby: {$max: '$updatedAt'}}},
        {$sort: {sortby: -1, _id: 1}},
        {$project: {name: '$_id', topics_size: 1, _id: 0}}
      ];
    }
    let result = await this.ctx.model.Topic.aggregate(pipeline);

    if (!name) {
      await this.service.cacheSvc.setex(cache_key, result);
    }
    return result;
  }


  /**
   * 查询用户创作的文章
   * @param {string} author_id 用户id
   * @param {boolean} is_oneself 是否是本人
   * @param {string} keyword 搜索的词
   * @param {string} tag_name 收藏标签名
   * @param {number} page_index
   * @param {number} page_size
   * @param {string} board_name 每个站点的子版块名
   */
  async listOriginalTopics(author_id, is_oneself, keyword, tag_name, page_index, page_size, board_name) {
    let queryObj = {author_id: author_id, tab: {$ne: 'share'}};
    // 如果作者不是本人, 那只能看公开的
    if (is_oneself == false) {
      queryObj.is_public = true;
    }
    if (tag_name) {
      queryObj.tags = tag_name;
    }
    if (board_name != '全部') {
      queryObj.boards = board_name;
    }
    if (keyword) {
      const realKeyword = keyword.replace(/\s+/g, '.*');
      if (is_oneself) {
        queryObj['$or'] = [
          {title: {'$regex': new RegExp(realKeyword, 'i')}},
          {content: {'$regex': new RegExp(realKeyword, 'i')}}
        ];
      } else {
        queryObj.title = {'$regex': new RegExp(realKeyword, 'i')};
      }
    }

    let opt = {sort: '-createdAt'};
    if (page_index && page_size) {
      opt.skip = (page_index - 1) * page_size;
      opt.limit = page_size;
    }

    let topics = await this.ctx.model.Topic.find(
      queryObj,
      '',
      opt
    );
    return topics;
  }


  /**
   * 统计用户创作的文章数量
   * @param {string} author_id 用户id
   * @param {boolean} is_oneself 是否是本人
   * @param {string} keyword 搜索的词
   * @param {string} tag_name 标签名
   * @param {string} board_name 子板块名
   */
  async countOriginalTopics(author_id, is_oneself, keyword, tag_name, board_name) {
    let queryObj = {author_id: author_id, tab: {$ne: 'share'}};
    // 如果作者不是本人, 那只能看公开的
    if (is_oneself == false) {
      queryObj.is_public = true;
    }
    if (tag_name) {
      queryObj.tags = tag_name;
    }
    if (board_name != '全部') {
      queryObj.boards = board_name;
    }
    if (keyword) {
      const realKeyword = keyword.replace(/\s+/g, '.*');
      if (is_oneself) {
        queryObj['$or'] = [
          {title: {'$regex': new RegExp(realKeyword, 'i')}},
          {brief: {'$regex': new RegExp(realKeyword, 'i')}}
        ];
      } else {
        queryObj.title = {'$regex': new RegExp(realKeyword, 'i')};
      }
    }

    return await this.ctx.model.Topic.count(queryObj);
  }
}

module.exports = TopicSvc;
